/*
 * Copyright (c) 2010 Philip Foulkes <philip.foulkes@gmail.com>
 *
 * This file is free software and is made available under the 
 * terms of the GNU General Public License, version 2, or at your
 * option, any later version
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */
 
#include <linux/module.h>

#include "mvrp_private.h"
#include "mvrp_mrpdu.h"
#include "mvrp.h"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Philip Foulkes <philip.foulkes@gmail.com>");
MODULE_DESCRIPTION("Multiple VLAN Registration Protocol");

/*
 * MVRP application
 */
struct mvrp
{
	struct mrp mrp; /* has to be the first member */
};


static struct mvrp mvrp;


// module parameters ===============================
static char * port_name = "eth0";
module_param (port_name, charp, S_IRUGO);
// =================================================


// ================================================================
// ops ============================================================
static int mvrp_mad_join_indication (struct mrp * mrp, int mrp_attribute_type, void * attribute_value, int four_packed_type, int new)
{
	u16 vid;
    
    MVRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "called");
    
    if (mrp_attribute_type == MVRP_ATTRIBUTE_TYPE_VID)
    {
        memcpy (&vid, attribute_value, MVRP_ATTRIBUTE_LENGTH_VID);
        vid = ntohs(vid);
        MVRP_PRINT_INFO ("%s %hu\n", "VID", vid);
    }
    else
    {
        MVRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "called with invalid mrp_attribute_type");
        return -EINVAL;
    }
    
	return 0;
}

static int mvrp_mad_leave_indication (struct mrp * mrp, int mrp_attribute_type, void * attribute_value, int four_packed_type)
{
	u16 vid;
    
    MVRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "called");
    
    if (mrp_attribute_type == MVRP_ATTRIBUTE_TYPE_VID)
    {
        memcpy (&vid, attribute_value, MVRP_ATTRIBUTE_LENGTH_VID);
        vid = ntohs(vid);
        MVRP_PRINT_INFO ("%s %hu\n", "VID", vid);
    }
    else
    {
        MVRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "called with invalid mrp_attribute_type");
        return -EINVAL;
    }
    
	return 0;
}

static int vid_compare (void * one, void * two) { return memcmp(one, two, MVRP_ATTRIBUTE_LENGTH_VID); }

static mrp_attribute_value_compare mvrp_get_attribute_value_compare (int attribute_type)
{
	if(attribute_type == MVRP_ATTRIBUTE_TYPE_VID)
        return vid_compare;
    else
    {
        MVRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "called with invalid attribute_type");
        return NULL;
    }
}

static int mvrp_get_attribute_length (int attribute_type)
{
	if(attribute_type == MVRP_ATTRIBUTE_TYPE_VID)
        return MVRP_ATTRIBUTE_LENGTH_VID;
    else
    {
        MVRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "called with invalid attribute_type");
        return -EINVAL;
    }
}

static int mvrp_increment_first_value (int attribute_type, void * first_value, int how_much)
{
    u16 vid;
    
    vid = 0;
    
    if(attribute_type == MVRP_ATTRIBUTE_TYPE_VID)
    {
        memcpy(&vid, first_value, MVRP_ATTRIBUTE_LENGTH_VID);
        vid = ntohs(vid);
        if (vid + how_much < MVRP_ATTRIBUTE_VALUE_VID_MIN || vid + how_much > MVRP_ATTRIBUTE_VALUE_VID_MAX)
            return -EINVAL;
        vid = htons(vid + how_much);
        memcpy(first_value, &vid, MVRP_ATTRIBUTE_LENGTH_VID);
    }
    else
    {
        MVRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "called with invalid attribute_type");
        return -EINVAL;
    }
    
    return 0;
}

static int mvrp_does_need_four_packed_events (int attribute_type) { return 0; }

static struct mrp_ops mvrp_mrp_ops = {
	.mrp_mad_join_indication 			= mvrp_mad_join_indication,
	.mrp_mad_leave_indication 			= mvrp_mad_leave_indication,
	.mrp_get_attribute_value_compare 	= mvrp_get_attribute_value_compare,
	.mrp_get_attribute_length 			= mvrp_get_attribute_length,
	.mrp_increment_first_value			= mvrp_increment_first_value,
	.mrp_does_need_four_packed_events 	= mvrp_does_need_four_packed_events,
};
// ================================================================
// ================================================================


/**
 * Indicates to the MAC Service provider that the MAC Service user wishes to receive frames destined for the
 * VLAN identified by the VID parameter.
 */
int mvrp_es_register_vlan_member(u16 vid)
{ 
    if (vid < MVRP_ATTRIBUTE_VALUE_VID_MIN || vid > MVRP_ATTRIBUTE_VALUE_VID_MAX)
        return -EINVAL;
    
    vid = htons(vid);
    
    return mrp_mad_join_request_notify  ((struct mrp *)&mvrp, MVRP_ATTRIBUTE_TYPE_VID, &vid, 0, 0);
}
EXPORT_SYMBOL(mvrp_es_register_vlan_member);

/**
 * Indicates to the MAC Service provider that the MAC Service user no longer wishes to receive frames
 * destined for the VLAN identified by the VID parameter.
 */
int mvrp_es_deregister_vlan_member(u16 vid)
{
	if (vid < MVRP_ATTRIBUTE_VALUE_VID_MIN || vid > MVRP_ATTRIBUTE_VALUE_VID_MAX)
        return -EINVAL;
        
    vid = htons(vid);
    
    return mrp_mad_leave_request_notify  ((struct mrp *)&mvrp, MVRP_ATTRIBUTE_TYPE_VID, &vid);
}
EXPORT_SYMBOL(mvrp_es_deregister_vlan_member);

static void mvrp_exit (void);

static int mvrp_init (void)
{
	int ret;
	
	MVRP_PRINT_INFO ("%s\n", "Initialisation start");
	
	ret = 0;
	
	memset (&mvrp, 0, sizeof (struct mvrp));
	
	if ((ret = mrp_init ((struct mrp *)&mvrp, mrp_application_address_mvrp, MRP_ETHERTYPE_MVRP, port_name, &mvrp_mrp_ops, MVRP_PROTOCOL_VERSION, 0)))
		goto fail;
        
	MVRP_PRINT_INFO ("%s\n", "Initialisation successful");
	
	return ret;
	
fail:
	MVRP_PRINT_INFO ("%s\n", "MVRP Initialisation Failed");
	mvrp_exit();
	return ret;
}

static void mvrp_exit (void)
{
	MVRP_PRINT_INFO ("%s\n", "Exit start");
	
	mrp_clean_up ((struct mrp *)&mvrp);
	
	MVRP_PRINT_INFO ("%s\n", "Exit end");
}

module_init (mvrp_init);
module_exit (mvrp_exit);
